<script setup lang="ts">
import { NewClient, type StreamType } from "@/utils/client";
import { useRoute } from "vue-router";
import {
  computed,
  onMounted,
  onUnmounted,
  reactive,
  ref,
  unref,
  watch,
} from "vue";
import { ftpStorage, serverStorage, webStorage } from "@/storage";
import { dayjs, ElMessage } from "element-plus";
import {
  bytesToSize,
  eachTree,
  findTree,
  toLinuxPath,
  toWindowPath,
  uniqueSet,
} from "@/utils";
import { basename, join } from "node:path";
import {
  ArrowLeft,
  Folder,
  List,
  Menu,
  Refresh,
  ArrowDown,
  SortDown,
  SortUp,
} from "@element-plus/icons-vue";
import { Session } from "@/utils/storage";
import { useEventListener } from "@vueuse/core";
import {
  onDownloadFiles,
  onFastGet,
  onStartExec,
  onUpLoadFiles,
} from "@/utils/upload";
import { emitter, openSelectDirectory } from "@/utils/mitt";
import FileImage from "@/components/FileImage.vue";
import ContextMenu from "@/components/ContextMenu.vue";
import type { ClientType, FileEntryWithType, SFTPType } from "@/utils/client";
import type { Terminal } from "@xterm/xterm";
import { toDateCommand } from "@/utils/command";
import SvgBack from "@/components/svg/SvgBack.vue";

interface Props {
  toolBack?: boolean;
  connection?: boolean;
  client?: ClientType;
  term?: Terminal;
  stream?: StreamType;
}

const props = withDefaults(defineProps<Props>(), {
  toolBack: true,
  connection: false,
});

// 当前路由
const route = useRoute();
// 排序
const orderBy = ref("");
// 连接
let startClient: ClientType | undefined;
// 视图模式
const viewMode = ref(props.connection ? "list" : "icon");
// 鼠标按下
const longPressedHook = ref(false);
// 鼠标按下时间
const longPressedHookTime = ref(Date.now());
// 画矩形的起始点
let oldTop = 0;
let oldLeft = 0;
// 矩形容器
const moveRef = ref<HTMLDivElement>();
// 矩形宽高坐标样式
const moveStyle = reactive({
  top: "0",
  left: "0",
  width: "0",
  height: "0",
  bottom: "0",
  right: "0",
});
// 文件系统区域
const ulRef = ref<HTMLUListElement>();
// 滚动区域
const scrollRef = ref<HTMLDivElement>();
// 右键菜单
const contextmenuRef = ref();
// 传递 web或server ID
const id = route.query.id || "";
// 前端还是后台
const type: string = String(route.query.type || "web");
// 选中当前文件
const active = ref<string[]>([]);
// 下拉坐标
const dropdown = reactive({ x: 0, y: 0 });
// 加载中
const loading = ref(false);
// 当前路径
const currentPath = ref("/");
// 当前FTP
const ftp = ref<DeployFtpType>({
  title: "成都147",
  id: "",
  host: "192.168.0.147",
  port: 22,
  username: "root",
  password: "hrz81788178.com",
  system: "linux",
});
// 当前web或server
const web = ref<WebType>({
  id: "",
  title: "成都147MIS",
  ftp_id: "test_1111",
  target: "/",
  source: "D:\\projects\\MisWWW\\vue-element-plus\\dist_mis",
  exclude: [],
  filename: "admin",
  writes: [],
});
// 是否按住Ctrl
const isControl = ref(false);
// 是否按住Shift
const isShift = ref(false);
// Shift多选的起始点
let startShift: number | undefined;
// 右键菜单
const contextMenuClickId = ref(0);

// SFTPWrapper
let sftp: SFTPType | undefined;

// 当前文件列表
const items = ref<FileEntryWithType[]>([]);

// 按住Shift的时候重置选中和起始点
watch(
  () => isShift.value,
  (value) => {
    if (value) {
      active.value.splice(1);
      if (active.value.length) {
        startShift = items.value.findIndex(
          (f) => f.filename === active.value[0]
        );
      }
    } else startShift = undefined;
  }
);

/**
 * 格式化为Linux的路径
 */
const formatPath = () => {
  if (ftp.value.system === "linux") {
    currentPath.value = toLinuxPath(unref(currentPath));
  } else {
    currentPath.value = toWindowPath(unref(currentPath));
  }
  if (!currentPath.value || currentPath.value === ".") {
    currentPath.value = "/";
  }
  return currentPath.value;
};

/**
 * 获取本地FTP对象
 */
const getFtp = () => {
  currentPath.value =
    Session.get(encodeURI(route.fullPath)) ||
    `${web.value.target}/${web.value.filename}`;
  const ftpItem = findTree(
    unref(ftpStorage),
    (item) => item.id === web.value.ftp_id
  );
  if (ftpItem) {
    ftp.value = ftpItem;
    main();
  }
};

/**
 * 刷新
 * @param p
 */
const onFetch = (p?: string) => {
  active.value.splice(0);
  if (p) currentPath.value = p;
  const path = formatPath();
  if (path) {
    loading.value = true;
    sftp?.readdir(path, (err, files) => {
      loading.value = false;
      if (err) {
        ElMessage.warning(`路径可能错误 => ${path}`);
        if (currentPath.value !== "/") {
          currentPath.value = "/";
          onFetch();
        }
      } else {
        onItemSort(files, false);
        items.value = files;
        Session.set(encodeURI(route.fullPath), unref(currentPath));
      }
    });
  }
};

/**
 * 入口
 */
const main = async () => {
  startClient = props.client || new NewClient();
  loading.value = true;
  if (props.connection) {
    // 已连接
    initSftp();
  } else {
    // 否者重新连接
    startClient
      ?.on("ready", () => {
        initSftp();
      })
      .connect(unref(ftp));
  }
  // 连接错误
  startClient?.on("error", () => {
    loading.value = false;
  });
};

/**
 * 连接文件服务器
 */
const initSftp = () => {
  startClient?.sftp((error, ftp) => {
    loading.value = false;
    sftp = ftp;
    if (error) {
      console.log("sftp错误", error);
    } else {
      onFetch();
    }
  });
};

/**
 * 双击
 * @param item
 */
const onDblclick = (item?: FileEntryWithType) => {
  const path = formatPath();
  if (item) {
    if (item.attrs.isDirectory()) {
      currentPath.value = join(path, item.filename);
      onFetch();
    } else {
    }
  } else {
    onBack();
  }
};

/**
 * 单击
 * @param item
 */
const onClick = (item: FileEntryWithType) => {
  const isExist = active.value.includes(item.filename);
  if (isControl.value) {
    if (isExist) {
      active.value = active.value.filter((f) => f !== item.filename);
    } else {
      active.value.push(item.filename);
    }
  } else if (isShift.value) {
    const currentIdx = items.value.findIndex(
      (f) => f.filename === item.filename
    );
    if (startShift === undefined) {
      startShift = currentIdx;
      active.value = [item.filename];
    } else {
      // 起点和终点
      const numbers = [startShift, currentIdx];
      const minIdx = Math.min(...numbers);
      const maxIdx = Math.max(...numbers);
      active.value = items.value
        .filter((f, idx) => f && idx >= minIdx && idx <= maxIdx)
        .map((v) => v.filename);
    }
  } else {
    active.value = [item.filename];
  }
};

/**
 * 右键菜单
 * @param event
 * @param item
 */
const onContextMenu = (event: MouseEvent, item?: FileEntryWithType) => {
  if (item && !active.value.includes(item.filename)) {
    active.value.splice(0);
    active.value.push(item.filename);
  }
  event.preventDefault();
  event.stopPropagation();
  const { clientX, clientY } = event;
  dropdown.x = clientX;
  dropdown.y = clientY;
  contextmenuRef.value.openContextmenu(active.value.length, item);
};

/**
 * 右键菜单回调
 * @param id
 */
const onContextMenuClick = (id: number) => {
  contextMenuClickId.value = id;
  if ([0, 1].includes(id)) {
    openSelectDirectory();
  } else if (id === 3) {
    onFetch();
  } else if (id === 5) {
    onDeleteExec();
  } else {
  }
};

/**
 * 用命令删除会快一点
 */
const onDeleteExec = () => {
  if (!active.value.length) {
    ElMessage.warning("没有选择文件");
    return;
  }
  const list = items.value.filter((f) => active.value.includes(f.filename));
  const path = formatPath();
  const commands: string[] = [];
  list.forEach((v) => {
    if (ftp.value.system === "linux") {
      commands.push(`rm -rf "${toLinuxPath(`${path}/${v.filename}`)}"`);
    } else {
      if (v.attrs.isDirectory()) {
        commands.push(`rd /s /q "${toWindowPath(`${path}\\${v.filename}`)}"`);
      } else {
        commands.push(`del /s /q "${toWindowPath(`${path}\\${v.filename}`)}"`);
      }
    }
  });
  const command = commands.join(" && ");
  startClient?.exec(command, (err, stream) => {
    if (err) throw err;
    stream
      .on("close", () => {
        stream.end(toDateCommand(ftp.value.system));
        ElMessage.success(`删除成功。`);
        onFetch();
      })
      .stdout.on("data", (data: string) => {
        console.log("OUTPUT: " + data);
      })
      .stderr.on("data", (data: string) => {
        console.log("STDERR: " + data);
      });
  });
};

/**
 * 返回
 */
const onBack = () => {
  const path = formatPath();
  if (path) {
    currentPath.value = join(path, "../") || "/";
    onFetch();
  }
};

/**
 * 格式化时间
 * @param time
 */
const ftTime = (time: number) => {
  return dayjs(time).format("YYYY-MM-DD HH:mm");
};

/**
 * 切换视图模式
 */
const onViewMode = () => {
  const v = unref(viewMode);
  viewMode.value = v === "icon" ? "list" : "icon";
};

/**
 * 重置矩形
 */
const onResetMove = () => {
  moveStyle.width = "0px";
  moveStyle.height = "0px";
  moveStyle.top = "0px";
  moveStyle.left = "0px";
};

/**
 * 全局事件监听
 */
const onEventListener = () => {
  // 开启文件拖拽
  useEventListener(scrollRef, "dragover", (event) => {
    event.stopPropagation();
    event.preventDefault();
  });

  // 拖拽结束
  useEventListener(scrollRef, "drop", (event) => {
    event.stopPropagation();
    event.preventDefault();
    const files = event.dataTransfer?.files || [];
    loading.value = true;
    emitter.emit("showLoading", { text: "正在上传选中的文件..." });
    onUpLoadFiles(
      formatPath(),
      Array.from(files).filter((f) => f.path),
      sftp
    )
      .then(onFetch)
      .finally(() => {
        loading.value = false;
        emitter.emit("hideLoading");
      });
  });

  // 鼠标移动
  useEventListener(scrollRef, "mousemove", (event) => {
    if (!longPressedHook.value) return;
    if (event.pageX < oldLeft) {
      //向左拖
      moveStyle.left = event.pageX + "px";
      moveStyle.width = oldLeft - event.pageX + "px";
    } else {
      moveStyle.width = event.pageX - oldLeft + "px";
    }
    if (event.pageY < oldTop) {
      //向上
      moveStyle.top = event.pageY + "px";
      moveStyle.height = oldTop - event.pageY + "px";
    } else {
      moveStyle.height = event.pageY - oldTop + "px";
    }
    event.preventDefault();
  });

  // 鼠标按下
  useEventListener(scrollRef, "mousedown", (event) => {
    if (event.button === 2) return;
    longPressedHookTime.value = Date.now();
    longPressedHook.value = true;
    oldTop = event.pageY;
    oldLeft = event.pageX;
    moveStyle.top = event.pageY + "px";
    moveStyle.left = event.pageX + "px";
  });

  // 鼠标抬起
  useEventListener(scrollRef, "mouseup", (event) => {
    if (event.button === 2) return;
    longPressedHook.value = false;
    if (!isControl.value && !isShift.value) active.value.splice(0);
    const diff = Date.now() - longPressedHookTime.value;
    if (diff <= 300) return onResetMove();
    // 获取元素的边界F
    const move = unref(moveRef);
    if (move) {
      const { top, height, width, left } = move.getBoundingClientRect();
      moveStyle.bottom = top + height + "px";
      moveStyle.right = left + width + "px";
    }
    findSelected();
  });

  // 鼠标离开
  useEventListener(window, "mouseleave", () => {
    onResetMove();
  });

  // 按键监听
  useEventListener(window, "keydown", (event) => {
    if (["ControlLeft", "ControlRight"].includes(event.code)) {
      isControl.value = true;
    } else if (["ShiftLeft", "ShiftRight"].includes(event.code)) {
      isShift.value = true;
    } else if (isControl.value && ["KeyA"].includes(event.code)) {
      active.value = items.value.map((v) => v.filename);
      event.preventDefault();
    } else if (isControl.value && ["KeyD"].includes(event.code)) {
      onDeleteExec();
      event.preventDefault();
    } else if (
      ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(event.code)
    ) {
      if (active.value.length === 1) {
        const index = items.value.findIndex(
          (f) => f.filename === active.value[0]
        );
        if (["ArrowUp", "ArrowLeft"].includes(event.code)) {
          const item = items.value[index - 1];
          if (item) active.value = [item.filename];
        } else {
          const item = items.value[index + 1];
          if (item) active.value = [item.filename];
        }
      } else if (!active.value.length && items.value.length) {
        if (["ArrowUp", "ArrowLeft"].includes(event.code)) {
          active.value = [items.value[items.value.length - 1].filename];
        } else {
          active.value = [items.value[0].filename];
        }
      }
    }
  });

  // 松开Control多选结束
  useEventListener(window, "keyup", (event) => {
    if (["ControlLeft", "ControlRight"].includes(event.code)) {
      isControl.value = false;
    } else if (["ShiftLeft", "ShiftRight"].includes(event.code)) {
      isShift.value = false;
    }
  });
};

/**
 * 压缩并下载
 * @param localPath
 */
const onShellZip = (localPath: string) => {
  loading.value = true;
  emitter.emit("showLoading", { text: "正在下载选中的文件..." });
  const path = formatPath();
  const fileNames = items.value.filter((f) =>
    active.value.includes(f.filename)
  );

  if (fileNames.length) {
    const baseName = basename(path);
    const suffix = ftp.value.system === "linux" ? ".tar.gz" : ".7z";
    const output = `${baseName}${suffix}`;
    const localOutput = `${localPath}/${baseName}${suffix}`;
    const remotePaths: string[] = [];
    fileNames.forEach((item) => {
      if (item.attrs.isDirectory()) {
        remotePaths.push(`"./${item.filename}/"`);
      } else {
        remotePaths.push(`"./${item.filename}"`);
      }
    });
    let command = "";
    if (ftp.value.system === "linux") {
      command = `cd ${path} && tar -czvf ${output} ${remotePaths.join(" ")}`;
    } else {
      command = `cd ${path} && 7z a -t7z ${output} ${remotePaths.join(" ")}`;
    }
    props.term?.write("\n\r\n\r");
    props.term?.write(`\x1b[32m启用子进程 ${command}\x1b[0m\n\r`);
    props.term?.write("\n\r\n\r");
    console.log(command);
    startClient?.shell({ term: "xterm" }, (error, channel) => {
      if (error) throw error;
      channel
        .on("close", () => {
          console.log("Stream :: close");
          channel.end();
          if (sftp) {
            emitter.emit("showLoading", { text: `${path}/${output}` });
            onFastGet(sftp, `${path}/${output}`, localOutput)
              .then(() => {
                ElMessage.success("下载成功");
                onStartExec(localPath);
              })
              .finally(() => {
                onRmShellGZip(`${path}/${output}`);
              });
          } else {
            onRmShellGZip(`${path}/${output}`);
          }
        })
        .on("data", (data: string) => {
          emitter.emit("showLoading", { text: data.toString() });
          props.term?.write(data.toString());
        })
        .stderr.on("data", (err: string) => {
          props.term?.write(err);
        });
      channel.write(command + "\n");
      channel.write("exit\n");
    });
  } else {
    ElMessage.warning("请选择文件");
  }
  if (!startClient) {
    loading.value = false;
    emitter.emit("hideLoading");
  }
};

/**
 * 删除下载后的压缩包
 * @param output
 */
const onRmShellGZip = (output: string) => {
  props.term?.write("\n\r\n\r");
  props.term?.write(`\x1b[32m下载成功，注销子进程并删除压缩包。 \x1b[0m\n\r`);
  props.term?.write("\n\r\n\r");
  loading.value = false;
  emitter.emit("hideLoading");
  let command;
  if (ftp.value.system === "linux") {
    command = `rm -rf ${toLinuxPath(output)}`;
  } else {
    command = `del ${toWindowPath(output)}`;
  }
  if (props.stream) {
    // 有终端使用终端
    props.stream.write(`${command}\n`);
  } else {
    startClient?.exec(command, (error, stream) => {
      if (error) throw error;
      stream
        .on("close", () => {
          console.log("Stream :: close");
          stream.end();
          props.stream?.write(toDateCommand(ftp.value.system));
        })
        .on("data", (data: string) => {
          console.log("STDOUT: " + data);
          props.term?.write(data);
        })
        .stderr.on("data", function (data) {
          console.log("STDERR: " + data);
        });
    });
  }
};

/**
 * 获取选中
 */
const findSelected = () => {
  const ul = unref(ulRef);
  const move = unref(moveRef);
  if (ul && move) {
    // 获取所有li元素
    const list: NodeListOf<HTMLLIElement> = ul.querySelectorAll(".file-ul-li");
    // 获取元素的边界F
    const moveRect = move.getBoundingClientRect();
    //判断每个块是否被遮罩盖住（即选中）
    list.forEach((element) => {
      // 获取元素的边界F
      const elementRect = element.getBoundingClientRect();
      if (
        elementRect.left < moveRect.right &&
        elementRect.right > moveRect.left &&
        elementRect.top < moveRect.bottom &&
        elementRect.bottom > moveRect.top
      ) {
        if (
          element.dataset.filename &&
          !active.value.includes(element.dataset.filename)
        )
          active.value.push(element.dataset.filename);
      }
    });
  }
  onResetMove();
};

/**
 * 当前服务器所有路径
 */
const allPaths = computed(() => {
  const alls = [...serverStorage.value, ...webStorage.value];
  const ls: {
    value: string;
    title: string;
    system: "linux" | "window";
    filename: string;
  }[] = [];
  eachTree(alls, (v) => {
    if (v.ftp_id === ftp.value.id) {
      const system = ftp.value.system;
      if (!ls.some((s) => s.value === v.target)) {
        ls.push({
          value:
            system === "window"
              ? toWindowPath(v.target)
              : toLinuxPath(v.target),
          title: v.title,
          filename: v.filename,
          system,
        });
      }
      ls.push({
        value:
          system === "window"
            ? toWindowPath(`${v.target}\\${v.filename || ""}`)
            : toLinuxPath(`${v.target}/${v.filename || ""}`),
        title: v.title,
        filename: v.filename,
        system,
      });
    }
  });
  return uniqueSet(ls);
});

/**
 * 选择路径
 * @param value
 */
const onCommand = (value: string) => {
  currentPath.value = value;
  onFetch();
};

/**
 * 刷新
 */
const onRefresh = () => {
  onFetch(`${web.value.target}/${web.value.filename}`);
};

/**
 * 延迟刷新（命令行执行过快可能刷新不及时时用到）
 */
const onTimeoutRefresh = () => {
  setTimeout(onRefresh, 500);
};

/**
 * 排序
 * @param list
 * @param isSwitch 是否切换
 */
const onItemSort = (list: FileEntryWithType[], isSwitch?: boolean) => {
  const by = orderBy.value;
  if (isSwitch) orderBy.value = by === "" ? "asc" : by === "asc" ? "desc" : "";
  if (orderBy.value === "desc") {
    list.sort((a, b) => {
      return a.attrs.mtime - b.attrs.mtime;
    });
  } else if (orderBy.value === "asc") {
    list.sort((a, b) => {
      return b.attrs.mtime - a.attrs.mtime;
    });
  } else {
    list.sort((a) => (a.attrs.isDirectory() ? -1 : 0));
  }
};

/**
 * 选择文件夹回调
 * @param localPath
 */
const onSelectedDirectory = (localPath: string) => {
  if (contextMenuClickId.value === 1) {
    // 压缩下载
    onShellZip(localPath);
  } else {
    // 普通下载
    loading.value = true;
    emitter.emit("showLoading", { text: "正在下载选中的文件..." });
    props.term?.write("\n\r\n\r");
    props.term?.write(`\x1b[32m正在下载中...请耐心等待。 \x1b[0m\n\r`);
    props.term?.write("\n\r\n\r");
    onDownloadFiles(
      formatPath(),
      localPath,
      items.value.filter((f) => active.value.includes(f.filename)),
      sftp
    )
      .then(() => {
        ElMessage.success("下载成功！");
        props.term?.write(
          `\x1b[32m下载完成。（如果觉得慢，请使用压缩下载） \x1b[0m\n\r`
        );
        props.term?.write("\n\r\n\r");
        props.stream?.write(toDateCommand(ftp.value.system));
        onStartExec(localPath);
      })
      .finally(() => {
        loading.value = false;
        emitter.emit("hideLoading");
      });
    // 处理文件路径...
  }
};

const emitKey = `on-directory-${route.fullPath}`;
onMounted(() => {
  if (route.query.ftp_id) {
    web.value.ftp_id = String(route.query.ftp_id || "test_1111");
    getFtp();
  } else if (type === "web") {
    const item = findTree(unref(webStorage), (item) => item.id === id);
    if (item) {
      web.value = item;
      getFtp();
    }
  } else if (type === "server") {
    const item = findTree(unref(serverStorage), (item) => item.id === id);
    if (item) {
      web.value = item;
      getFtp();
    }
  }
  onEventListener();
  emitter.on(emitKey, onSelectedDirectory);
});

onUnmounted(() => {
  emitter.off(emitKey);
  sftp?.end();
  sftp?.destroy();
  startClient?.destroy();
});

defineExpose({ onRefresh, onTimeoutRefresh });
</script>

<template>
  <div class="file-index">
    <div class="flex align-center e-border-bottom pa2">
      <el-button
        circle
        :icon="ArrowLeft"
        :disabled="loading"
        class="mr5 ml0"
        @click="onBack"
        v-if="toolBack"
      />
      <el-button
        circle
        :icon="viewMode === 'icon' ? Menu : List"
        class="mr5 ml0"
        @click="onViewMode()"
      />
      <el-button
        circle
        :icon="Folder"
        :disabled="loading"
        class="mr5 ml0"
        @click="onRefresh"
      />
      <el-button
        circle
        :icon="Refresh"
        :loading="loading"
        class="mr5 ml0"
        @click="onFetch()"
      />
      <el-input
        v-model="currentPath"
        placeholder="请输入路径"
        class="flex-grow-1"
        @keyup.enter="onFetch()"
        :disabled="loading"
      >
        <template #append>
          <el-dropdown trigger="click" @command="onCommand" :disabled="loading">
            <el-button :icon="ArrowDown"></el-button>
            <template #dropdown>
              <el-dropdown-menu style="max-height: 70vh;">
                <el-dropdown-item
                  v-for="(item, index) in allPaths"
                  :key="index"
                  :command="item.value"
                  class="value font-12"
                >
                  <span class="color-primary font-bold mr5">{{
                    item.value
                  }}</span>
                  <span class="color-info mr5">{{ item.title }}:</span>
                  <span class="color-danger" v-if="item.filename"
                    >[{{ item.filename }}]</span
                  >
                </el-dropdown-item>
              </el-dropdown-menu>
            </template>
          </el-dropdown>
        </template>
      </el-input>
    </div>
    <el-scrollbar
      class="flex-grow-1 scroll-bar"
      ref="scrollRef"
      @contextmenu="onContextMenu($event)"
    >
      <ul ref="ulRef" class="file-ul" :class="viewMode">
        <li
          class="file-ul-li cursor-pointer user-select-none"
          @dblclick="onDblclick()"
        >
          <template v-if="viewMode === 'icon'">
            <SvgBack class="icon" />
            <div v-if="viewMode === 'icon'" class="font-10 color-info">0 B</div>
            <div v-if="viewMode === 'icon'" class="font-10 color-info">
              {{ ftTime(Date.now()) }}
            </div>
            <div class="text text-ellipsis color-info">返回</div>
          </template>
          <template v-else-if="viewMode === 'list'">
            <div class="title">
              <SvgBack class="icon" />
              <div class="text text-ellipsis color-info">...</div>
            </div>
            <!--            <div class="font-10 color-info flex-shrink">大小</div>-->
            <el-button
              :icon="
                orderBy === 'desc'
                  ? SortUp
                  : orderBy === 'asc'
                  ? SortDown
                  : Folder
              "
              text
              class="font-10 color-info flex-shrink"
              @click.stop="onItemSort(items, true)"
              @dblclick.stop=""
            >
              时间
            </el-button>
          </template>
        </li>
        <li
          v-for="(item, index) in items"
          :key="index"
          class="file-ul-li user-select-none"
          :class="{
            'cursor-pointer': item.attrs.isDirectory(),
            active: active.includes(item.filename),
          }"
          @click.stop="onClick(item)"
          @dblclick="onDblclick(item)"
          @contextmenu.stop="onContextMenu($event, item)"
          :data-filename="item.filename"
        >
          <template v-if="viewMode === 'icon'">
            <FileImage :item="item" />
            <div class="font-10 color-info">
              {{ bytesToSize(item.attrs.size) }}
            </div>
            <div class="font-10 color-info">
              {{ ftTime(item.attrs.mtime * 1000) }}
            </div>
            <div class="text text-ellipsis" :title="item.filename">
              {{ item.filename }}
            </div>
          </template>
          <template v-else-if="viewMode === 'list'">
            <div class="title flex-grow-1">
              <FileImage :item="item" />
              <div class="ml5" style="overflow: hidden">
                <div class="text text-ellipsis" :title="item.filename">
                  {{ item.filename }}
                </div>
                <div class="font-10 color-info text-ellipsis">
                  {{ bytesToSize(item.attrs.size) }}
                </div>
              </div>
            </div>
            <!--            <div class="font-10 color-info flex-shrink">{{ bytesToSize(item.attrs.size) }}</div>-->
            <div class="font-10 color-info flex-shrink time">
              {{ ftTime(item.attrs.mtime * 1000) }}
            </div>
          </template>
        </li>
        <li style="height: 200px; width: 100%"></li>
      </ul>
    </el-scrollbar>
    <ContextMenu
      :dropdown="dropdown"
      ref="contextmenuRef"
      @onContextMenuClick="onContextMenuClick"
    />
    <!-- 鼠标拖拽出的遮罩 （定位为  position:absolute）-->
    <!-- 遮罩最好是在绑定了mouseover事件的元素内部，并且不要阻止遮罩的冒泡事件。这样鼠标移到了遮罩上面，依然可以利用冒泡执行父元素的mouseover事件，就不会出现遮罩只能扩大，不能缩小的情况了（亲自试过） -->
    <div
      ref="moveRef"
      class="div-move"
      :style="moveStyle"
      v-if="longPressedHook"
    ></div>
  </div>
</template>

<style scoped>
@import url("FileIndex.css");
</style>
